#include <config.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/asm.h>

#include "ns16550.h"

#define msize		s2

/*
 * Register usage:
 *
 *	s0	link versus load offset, used to relocate absolute adresses.
 *	s1	ddr param
 *	s2	memory size.
 * s3 dimm param
 */

#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)
	.set   mips64
FEXPORT(lowlevel_init)
	move  fp, ra
	nop
	move  ra, fp
	jr fp
	nop
#else

	.set   mips64
FEXPORT(lowlevel_init)
	move   fp, ra

#if 1
	move   s0, zero

	mfc0   t0, CP0_STATUS
	li     t1, 0x64000000|ST0_KX|ST0_SX|ST0_UX|ST0_BEV      # {cu3,cu2,cu1,cu0}<={0110, status_fr<=1,0xe0 to enable 64bit space
	or     t0, t0, t1
	mtc0   t0, CP0_STATUS
	mtc0   zero, CP0_CAUSE
#endif

	//SPI init
	li   v0, 0xbfff0220
	li   v1, 0xff
	sb   v1, 0x5(v0) //set all spi cs to 1, default input
	li   v1, 0x47
	sb   v1, 0x4(v0) //spi speedup
	li   v1, 0x01
	sb   v1, 0x6(v0)

	//config bar for APB (必须配置成0x1fe00000 ?)
	li   t0, 0xba001000
	li   t1, 0x1fe00000
	sw   t1, 0x10(t0)
	lw   t1, 0x04(t0)
	or   t1, t1, 0x2
	sw   t1, 0x04(t0)

#if 1
	/*
	* Config SATA PHY: cpu复位后sata控制器默认使用外部晶振，
	* 但某些板卡使用内部晶振，需要修改寄存器切换到内部晶振模式，如果切换时间比较迟，
	* 如uboot启动后，这会导致驱动探测sata盘时不稳定，出现初始化失败的问题，
	* 所以这里把sata phy的初始化代码提前到汇编中进行，尽早切换晶振模式，减少不稳定。
	* 另外，建议sata使用外部晶振
	* 230508
	* 发现依旧存在 sata 不被uboot识别的问题
	* 这次改动为 先关闭 PHY (见寄存器的 63位)
	* 这样直接断电 sata phy 然后 切换 内部时钟
	* 然后再 开 sata phy
	* 同时 关于 时钟使能问题
	* 先禁用时钟
	* 先 解除reset phy 控制器
	* 再 开时钟
	* 再 解除reset LANE0
	* 同时 建议 时钟配置完之后延迟一段时间 有利于时钟稳定
	*/
	li   t1, 0xbfe10450

	li   t0, 0xB0c31cf9
	sw   t0, 0x4(t1)
	li   t0, 0x30c31cf9
	sw   t0, 0x4(t1)
	sync
	li   t0, 0xf300740c
	sw   t0, 0x0(t1)
	sync

#ifdef LOONGSON_SATA_USED_INTERNRL_CLK
	ld   a0, 0x0(t1)
	li   a1, 0x2
	not  a1, a1
	and  a0, a0, a1
	sd   a0, 0x0(t1)
	sync
#endif

	ld   a0, 0x0(t1)
	li   a1, 0x4
	or   a0, a1
	sd   a0, 0x0(t1)
	sync

	ld   a0, 0x0(t1)
	li   a1, 0x1
	or   a0, a1
	sd   a0, 0x0(t1)
	sync

	ld   a0, 0x0(t1)
	li   a1, 0x8
	or   a0, a1
	sd   a0, 0x0(t1)
	sync

	/* Config SATA TX signal*/
	dli   t0, 0x1403f1002
	sd   t0, 0x8(t1)
#endif

#if 1
	bal   ls2k_version
	nop
	bnez  v0, 2f
	nop

	//fix the hardware poweroff error.
	mfc0  t0, $15, 1      #EBASE
	andi  t0, t0, 0x3ff
	bnez  t0, 2f
	nop

	li    t0, 0xbfe0700c
	lw    t1, 0x0(t0) //APCI PM1_STS
	and   t2, t1,(1 << 11)
	beqz  t2, 2f
	nop

	li    t0, 0xbfe0700c
	lw    t1, 0x0(t0)
	sw    t1, 0x0(t0)
	li    t2, 0x3c00
	li    t0, 0xbfe07014
	sw    t2, 0x0(t0) //ACPI PM1_CNT
2:
#endif

	//关闭处理器store操作自动写合并功能
	mfc0  t0, $16, 6		#Store fill
	li    t1, 0xfffffeff
	and   t0, t1, t0
	mtc0  t0, $16, 6		#Store fill

cp0_main:
	mfc0    t0, $15, 1      #EBASE
	andi    t0, t0, 0x3ff
	bnez    t0, wait_for_smp
	nop

#ifdef BEEP_DEBUG_GPIO
	bal	beep_on
	nop

	li	a0, 0x2000
1:
	addiu	a0, -1
	nop
	bnez	a0, 1b
	nop
	bal	beep_off
	nop
#endif
#if 0
	//xwr pcie signal test
	li t0, 0xbfe10000

	li	t1, 0xc2492331
	sw	t1, 0x580(t0)
	sw	t1, 0x5a0(t0)

	li	t1, 0xff3ff0a8
	sw	t1, 0x584(t0)
	sw	t1, 0x5a4(t0)

	li	t1, 0x27fff
	sw	t1, 0x588(t0)
	sw	t1, 0x5a8(t0)

	/* mtf add for cfg pcie */
	li  t0, 0xbfe10590
	dli t1, 0x14fff1002
	sd  t1, 0x0(t0)
	sd  t1, 0x20(t0)

	dli t1, 0x14fff1102
	sd  t1, 0x0(t0)
	sd  t1, 0x20(t0)

	dli t1, 0x14fff1202
	sd  t1, 0x0(t0)
	sd  t1, 0x20(t0)

	dli t1, 0x14fff1302
	sd  t1, 0x0(t0)
	sd  t1, 0x20(t0)

	li  t0, 0xbfe10430
	lw  t1, 0x0(t0)
	or  t1, t1, 0x30000	//pcie enable
	sw  t1, 0x0(t0)

	//pcie1 port0
	dli t0, 0x900000fe0800680c
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe0700681c
	lw  t2, 0x0(t0)
	li  t1, (0x1 << 26)
	or  t2, t1
	sw  t2, 0x0(t0)

	dli t0, 0x900000fe00006800
	li  t1, 0x10000000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x10000000
	or  t0, t0, t1

	li  t1, (0x7<<18)|(0x7<<2)
	not t1, t1
	lw  t2, 0x54(t0)
	and t2, t2, t1
	sw  t2, 0x54(t0)

	lw  t2, 0x58(t0)
	and t2, t2, t1
	sw  t2, 0x58(t0)

	dli t1, 0xff204f
	sw  t1, 0x0(t0)

	//pcie1 port1
	dli t0, 0x900000fe0800700c
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe0700701c
	lw  t2, 0x0(t0)
	li  t1, (0x1 << 26)
	or  t2, t1
	sw  t2, 0x0(t0)

	dli t0, 0x900000fe00007000
	li  t1, 0x10100000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x10100000
	or  t0, t0, t1

	li  t1, (0x7<<18)|(0x7<<2)
	not t1, t1
	lw  t2, 0x54(t0)
	and t2, t2, t1
	sw  t2, 0x54(t0)

	lw  t2, 0x58(t0)
	and t2, t2, t1
	sw  t2, 0x58(t0)

	dli t1, 0xff204c
	sw  t1, 0x0(t0)

	//pcie0 port0
	dli t0, 0x900000fe0800480c	//other pcie controller
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe0700481c
	lw  t2, 0x0(t0)
	li  t1, (0x1 << 26)
	or  t2, t1
	sw  t2, 0x0(t0)

	dli t0, 0x900000fe00004800
	li  t1, 0x11000000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x11000000
	or  t0, t0, t1

	li  t1, (0x7<<18)|(0x7<<2)
	not t1, t1
	lw  t2, 0x54(t0)
	and t2, t2, t1
	sw  t2, 0x54(t0)

	lw  t2, 0x58(t0)
	and t2, t2, t1
	sw  t2, 0x58(t0)

	dli t1, 0xff204c
	sw  t1, 0x0(t0)

	//pcie0 port1
	dli t0, 0x900000fe0800500c	//other pcie controller
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe0700501c
	lw  t2, 0x0(t0)
	li  t1, (0x1 << 26)
	or  t2, t1
	sw  t2, 0x0(t0)

	dli t0, 0x900000fe00005000
	li  t1, 0x11100000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x11100000
	or  t0, t0, t1

	li  t1, (0x7<<18)|(0x7<<2)
	not t1, t1
	lw  t2, 0x54(t0)
	and t2, t2, t1
	sw  t2, 0x54(t0)

	lw  t2, 0x58(t0)
	and t2, t2, t1
	sw  t2, 0x58(t0)

	dli t1, 0xff204c
	sw  t1, 0x0(t0)

	//pcie0 port2
	dli t0, 0x900000fe0800580c	//other pcie controller
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe00005800
	li  t1, 0x11200000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x11200000
	or  t0, t0, t1

	dli t1, 0xff204c
	sw  t1, 0x0(t0)

	//pcie0 port3
	dli t0, 0x900000fe0800600c	//other pcie controller
	li  t1, 0xfff9ffff
	lw  t2, 0x0(t0)
	and t1, t1, t2
	or  t1, 0x20000
	sw  t1, 0x0(t0)

	dli t0, 0x900000fe00006000
	li  t1, 0x11300000
	sw  t1, 0x10(t0)

	dli t0, 0x9000000000000000
	li  t1, 0x11300000
	or  t0, t0, t1

	dli t1, 0xff204c
	sw  t1, 0x0(t0)
#endif
#ifdef BOARD_LS2K_PC_EVB
	bal watchdog_close
	nop
#endif

	//初始化调试串口，2k没配置前的启动频率默认100MHz
	li  a0, (OSC_CLK/16)/CONFIG_BAUDRATE
	bal initserial
	nop
	PRINTSTR("\r\nserial init ok\r\n")
	nop

	//配置2k的cpu频率、内存频率、apb频率等
#include "ls2k_clk_config.S"

#if 1
	// Fix the Gmac0  multi-func to enable Gmac1
	li   t0, 0xbfe13800
	dli  a0, 0xffffff0000ffffff
	sd   a0, 0x08(t0)

	li   t0, 0xba001800
	li   a0, 0x0080ff08
	sw   a0, 0x0c(t0)
#endif

#if 1
	// Set the invalid BAR to read only
	li   t0, 0xbfe13800
	dli  a0, 0xffffff0000fffff0
	sd   a0, 0x00(t0)
	sd   a0, 0x08(t0)
	sd   a0, 0x10(t0)
	sd   a0, 0x18(t0)
	sd   a0, 0x20(t0)
	sd   a0, 0x28(t0)
	sd   a0, 0x30(t0)
	sd   a0, 0x38(t0)
	sd   a0, 0x40(t0)
	sd   a0, 0x48(t0)
	sd   a0, 0x50(t0)
#endif

	/* enable kseg0 cachability */
	mfc0 a0, CP0_CONFIG
	ori  a0, a0, 0x3      //cached
	mtc0 a0, CP0_CONFIG

//##########################################
//DDR config start
//cxk
#include "mm/lsmc_ddr_param_define.h"
#include "mm/ddr_config_define.h"
//#define DDR_DLL_BYPASS
#define DISABLE_DIMM_ECC
#ifdef  ARB_LEVEL
#define AUTO_ARB_LEVEL
#endif
#ifdef  AUTO_ARB_LEVEL
//#define CHECK_ARB_LEVEL_FREQ
#ifdef  AUTO_DDR_CONFIG
#define CHECK_ARB_LEVEL_DIMM
#endif
//#define DEBUG_AUTO_ARB_LEVEL
#endif
//#define DEBUG_DDR
//#define DEBUG_DDR_PARAM
//#define PRINT_DDR_LEVELING

#ifdef  AUTO_DDR_CONFIG
#include "mm/i2c.h"
//	enable i2c0, i2c1
	li  t0, 0xbfe10420
	lw  t1, 0x0(t0)
	or  t1, t1, 0x0c00
	sw  t1, 0x0(t0)
#endif

	TTYDBG("\r\nInit Memory begin, wait a while......\r\n")
####################################
	move   msize, $0
	move   s3, $0
//!!!!important--s1 must be correctly set
#ifdef  AUTO_DDR_CONFIG
	dli   s1, 0xff100004  //set use MC1 or MC0 or MC1/0 and give All device id
#elif defined(DDR_S1)
	dli   s1, DDR_S1
#else
	//dli     s1, 0xc2e30400c2e30404
	//dli     s1, 0xc1a10404
	//dli	s1, 0xc0a10400c0a10400
	//dli	s1, 0xc0a18404
	//dli	s1, 0xf0a31004
	//dli	s1, 0xc1a10404
	dli   s1, ( MC_SDRAM_TYPE_DDR3    /* sdram type: DDR3/DDR2 */ \
		| MC_DIMM_ECC_NO        /* dimm ECC: YES/NO */ \
		| MC_DIMM_BUF_REG_NO    /* dimm buffer register: YES/NO, for RDIMM use YES, all else use NO*/ \
		| MC_DIMM_WIDTH_64      /* memory data width: 64/32 */ \
		| MC_SDRAM_ROW_16       /* sdram row address number: 15~11 */ \
		| MC_SDRAM_COL_10       /* sdram column address number: 12~9 */ \
		| MC_SDRAM_BANK_8       /* sdram bank number: 8/4 */ \
		| MC_ADDR_MIRROR_NO    /* for standard DDR3 UDIMM, use YES, else use NO */ \
		| MC_SDRAM_WIDTH_X16     /* SDRAM device data width: 8/16 */ \
		| MC_USE_CS_0         /* the CS pins the sdram connected on(split by '_', from small to big) */ \
		| MC_MEMSIZE_(8)        /* MC memory size, unit: 512MB */ \
		| USE_MC_0)
#endif

#include "mm/loongson3_ddr2_config.S"

	/*judge the node0 whether have memory*/
	and     a0, msize, 0xff

	//close default internal mapping in ddr controller
	li      t0, 0xbfe10424
	lb      a0, 0x1(t0)
	and     a0, a0, 0xfd
	ori     a0, a0, 0x01
	sb      a0, 0x1(t0)
	sync

	TTYDBG("\r\nInit Memory success\r\n")

bootnow:
	move  k1, msize
	move  ra, fp
	jr    ra
	nop

#endif //#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)

	.global wait_for_smp;
	.global wait_for_smp_call;
wait_for_smp:
	mfc0  t1, CP0_CONFIG
	ori   t1, t1, 0x3
	mtc0  t1, CP0_CONFIG
	dla    t1, 1f
	addu  t1, s0
	li    v0, 0x9fffffff
	and   t1, v0
	jr    t1
	nop
1:
	li    t0, 0xbfe11120
	sd    zero, 0(t0)
	sd    t1, 8(t0)

wait_for_smp_call:
1:
	ld    t1, 0(t0)
	beqz  t1, 1b
	ld    sp, 8(t0)
	sd    zero, 0(t0)
	ld    gp, 16(t0)

	jr    t1
	nop

#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)

#else

LEAF(initserial)
.set noat
	move AT, ra

	li   v0, UART_BASE_ADDR
	li   v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
	sb   v1, NSREG(NS16550_FIFO)(v0)
	li   v1, CFCR_DLAB                  #DLAB
	sb   v1, NSREG(NS16550_CFCR)(v0)
	sb   a0, NSREG(NS16550_DATA)(v0)
	srl  a0, 8
	sb   a0, NSREG(NS16550_IER)(v0)     #set BRDH
	li   v1, CFCR_8BITS                 #8bit
	sb   v1, NSREG(NS16550_CFCR)(v0)
	li   v1, MCR_DTR|MCR_RTS
	sb   v1, NSREG(NS16550_MCR)(v0)
	li   v1, 0x0
	sb   v1, NSREG(NS16550_IER)(v0)

	jr   ra
	nop
.set at
END(initserial)

LEAF(printk)
	.set noreorder
	move  a2, ra
	move  a1, a0
	lbu   a0, 0(a1)
1:
	beqz  a0, 2f
	nop
	bal   tgt_putchar
	nop
	addiu a1, 1
	lbu   a0, 0(a1)
	b     1b
	nop
2:
	jr    a2
	nop
	.set reorder
END(printk)

tgt_putchar:
	dla   v0, UART_BASE_ADDR
1:
	lbu  v1, NSREG(NS16550_LSR)(v0)
	and  v1, LSR_TXRDY
	beqz v1, 1b
	nop

	sb   a0, NSREG(NS16550_DATA)(v0)
	jr   ra
	nop

	.rdata
hexchar:
	.ascii	"0123456789abcdef"
	.text

#ifdef PRINT_MSG
LEAF(hexserial)
	move  a2, ra
	move  a1, a0
	li    a3, 8
1:
	rol   a0, a1, 4
	move  a1, a0
	and   a0, 0xf
	dla    t0, hexchar
	addu  t0, s0
	addu  t0, a0
	lbu   a0, 0(t0)
	bal   tgt_putchar

	addu  a3, -1
	bnez  a3, 1b
	nop

	jr    a2
	nop
END(hexserial)
#else
LEAF(hexserial)
	move  a2, ra
	jr    a2
	nop
END(hexserial)
#endif

LEAF(hexserial64)
	move	ta3, ra
	jr	ta3
   nop
END(hexserial64)

LEAF(ls2k_version)
	mfc0  a0, CP0_PRID
	andi  a0, a0, 0xf
	li    a1, 0x3
	bne   a0, a1, 2f
	li    v0, 0
	li    v0, 0x1
2:
	j     ra
	nop
END(ls2k_version)

#ifdef BEEP_DEBUG_GPIO
LEAF(beep_on)
	li  t1, 0xbfe10500 + BEEP_DEBUG_GPIO*4/32
	li  v1, (1<<(BEEP_DEBUG_GPIO&31))
	lw  t0, 0(t1)
	or  t0, v1
	xor t0, v1
	sw  t0, 0(t1)
	lw  t0, 0x10(t1)
	or  t0, v1
	sw  t0, 0x10(t1)

	jr  ra
	nop
END(beep_on)

LEAF(beep_off)
	li  t1, 0xbfe10500 + BEEP_DEBUG_GPIO*4/32
	li  v1, (1<<(BEEP_DEBUG_GPIO&31))
	lw  t0, 0(t1)
	or  t0, v1
	xor t0, v1
	sw  t0, 0(t1)
	lw  t0, 0x10(t1)
	or  t0, v1
	xor t0, v1
	sw  t0, 0x10(t1)

	jr  ra
	nop
END(beep_off)
#endif

#ifdef BOARD_LS2K_PC_EVB
LEAF(watchdog_close)
	//disable watch DOG.
	//gpio 3 output zero
	li  t1, 0xbfe10500

	li  t2, (1 << 3)
	not t2, t2
	lw  t3, 0x0(t1)
	and t2, t3
	sw  t2, 0x0(t1)

	li  t2, (1 << 3)
	lw  t3, 0x10(t1)
	not t2, t2
	and t2, t3
	sw  t2, 0x10(t1)
	nop

	jr  ra
	nop
END(watchdog_close)
#endif

#ifdef  AUTO_DDR_CONFIG
#include "mm/i2c.S"
#include "mm/detect_node_dimm_all.S"
#endif

#include "mm/ls3A8_ddr_config.S"
#ifdef DDR3_DIMM
#include "mm/loongson3C_ddr3_leveling.S"
#endif

    .align  5
#if defined(AUTO_DDR_CONFIG)
#include "loongson_mc2_param.S"
#else
#include "loongson_mc2_param-ddr16bit.S"
#endif

#ifdef  ARB_LEVEL
	.text
	.global c0_mc0_level_info
	.global c0_mc1_level_info
#ifdef  MULTI_CHIP
	.global c1_mc0_level_info
	.global c1_mc1_level_info
#endif

#include "mm/loongson3A3_ddr_param.lvled.S"

#endif

#endif //#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)